﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.ComponentModel;

namespace ircclient
{
    enum typeMessage { chat, privat, chatnotice, privatnotice, ctcp, ctcpnotice, server };
    class Server
    {
        private string ServerName;
        private string host;
        private int port;
        private StreamReader reader;
        private StreamWriter writer;
        private string rawServer;
        private TcpClient IRCConnect;
        private Thread ConnectThread;
        private FormMain ownerForm;
        private Thread ParsingThread;
        public List<string> commands = new List<string>();

        private string myNick = "Tester";
        private string quitMess = "Bye";

        private FormChat sysWindow;

        private List<Channel> channel = new List<Channel>();
        private List<Privat> privat = new List<Privat>();

        public class MessageArg : EventArgs
        {
            public MessageArg(string s, string t, string m, typeMessage type)
            {
                target = t;
                source = s;
                message = m;
                this.type = type;
            }
            private string message;
            private string target;
            private string source;
            private typeMessage type;

            public string Message
            {
                get { return message; }
                set { message = value; }
            }
            public string Target
            {
                get { return target; }
                set { target = value; }
            }
            public string Source
            {
                get { return source; }
                set { source = value; }
            }
            public typeMessage Type
            {
                get { return type; }
                set { type = value; }
            }

        }
        public event EventHandler<MessageArg> onMessage;

        protected virtual void SendMessage(MessageArg e)
        {
            EventHandler<MessageArg> handler = onMessage;

            if (handler != null)
            {

                handler(this, e);
            }
        }
        public Server(string host, int port, FormMain owner)
        {
            FormChat form = new FormChat();
            // Set the Parent Form of the Child window.
            form.MdiParent = owner;
            // Display the new form.
            form.Show();
            ownerForm = owner;
            this.host = host;
            this.port = port;
            sysWindow = form;
            ownerForm.AddNewSwitchButton("System");
            form.Closing += new CancelEventHandler(CloseServ);
            ParsingThread = new Thread(new ThreadStart(Parsing));
            ParsingThread.IsBackground = true;
            ParsingThread.Start();

        }
        public void Connect()
        {
            ConnectThread = Thread.CurrentThread;
            OutSystemWindow("Server - " + host + ":" + port);

            try
            {
                IRCConnect = new TcpClient(host, port);
            }
            catch (SocketException e)
            {
                OutSystemWindow("ОШИБКА " + e.Message);
                return; 
            }

            NetworkStream stream = IRCConnect.GetStream();

            reader = new StreamReader(stream, Encoding.GetEncoding("windows-1251"));
            writer = new StreamWriter(stream, Encoding.GetEncoding("windows-1251"));
            writer.WriteLine("NICK " + myNick);
            writer.Flush();
            writer.WriteLine("USER user host server :real");
            writer.Flush();

            while (true)
            {
                try
                {
                    rawServer = reader.ReadLine();
                }
                catch (System.IO.IOException e)
                {
                    OutSystemWindow("ОШИБКА " + e.Message);
                    break;
                }
                if (rawServer == "" || rawServer == "\n")
                {
                    Thread.Sleep(200);
                    continue;
                }

                if (rawServer.IndexOf("PING") == 0)
                {
                    rawServer = rawServer.Replace("PING", "PONG");
                    writer.WriteLine(rawServer);
                    writer.Flush();
                    continue;
                }
                else
                    commands.Add(rawServer);

            } //  while (true)
            
        }
        private void AddChan(string chanName)
        {
            Channel ch = new Channel(chanName, ownerForm, this);
            ch.RaiseCustomEvent += new EventHandler<Channel.CustomEventArgs>(CommandFromChan);
            channel.Add(ch);
            ownerForm.AddNewSwitchButton(chanName);
        }
        private void AddPrivate(string NickName)
        {
            bool flag = false;
            foreach (Privat ch in privat)
            {
                if (ch.Name == NickName)
                    flag = true;
            }
            if (flag == false)
            {
                Privat ch = new Privat(NickName, ownerForm, this);
                ch.RaiseCustomEvent += new EventHandler<Privat.CustomEventArgs>(CommandFromChan);
                privat.Add(ch);
                ownerForm.AddNewSwitchButton(NickName);
            }
        }

        void CommandFromChan(object sender, Channel.CustomEventArgs e)
        {
            writer.WriteLine(e.Message);
            writer.Flush();
            string[] cmd = e.Message.Split(new char[] { ' ' }, 3);
            if (cmd[0] == "PART")
            {
                if (cmd[1].IndexOf('#') == 0 || cmd[1].IndexOf('&') == 0)
                {
                    for (int i = 0; i < channel.Count(); i++)
                    {
                        if (channel[i].Name == cmd[1])
                            channel.RemoveAt(i);
                    }
                }
                else
                {
                    for (int i = 0; i < privat.Count(); i++)
                    {
                        if (privat[i].Name == cmd[1])
                            privat.RemoveAt(i);
                    }
                }
                ownerForm.DelSwitchButton(cmd[1]);
            }
        }

        private void OutSystemWindow(string text)
        {
            sysWindow.AddTextToChat(text + "<br>");
        }

        private void Parsing()
        {
            while(true)
            {
                if (commands.Count == 0)
                {
                    Thread.Sleep(200);
                    continue;
                }

                OutSystemWindow(commands[0]);
                string[] cmdColon = commands[0].Split(new char[] { ':' }, 3);
                string[] cmdspace = commands[0].Split(new char[] {' '},5);
                string cmdSource = "";
                string firstchan = "";

                if ( -1 != commands[0].IndexOf('#'))
                    firstchan = "#"+commands[0].Split(new char[] { '#' }, 2)[1].Split(new char[] {' '},2)[0];
                string cmd;
                if (cmdspace[0].IndexOf(':') == 0)
                {
                    cmdSource = cmdspace[0].Substring(1);
                    cmd = cmdspace[1];
                }
                else
                    cmd = cmdspace[0];

                string sourceNick = cmdSource.Split('!')[0];
                if (cmd == "JOIN")
                {
                    bool flag = false;
                    foreach (Channel ch in channel)
                    {
                        if(0 == String.Compare(ch.Name, firstchan, true))
                        {
                            ch.addUser(sourceNick);
                            flag = true;
                        }
                    }

                    if (flag == false)
                    {
                        AddChan(firstchan);
                        if(sourceNick != myNick)
                            channel.Last().addUser(sourceNick);
                    }

                }
                else if (cmd == "PRIVMSG" || cmd == "NOTICE")
                {
                    bool flag = (cmd == "PRIVMSG");
                    typeMessage type;
                    if (cmdSource.IndexOf('!') == -1)
                    {
                        OutSystemWindow(String.Format("< {0} > {1}", cmdSource, cmdColon[2]));
                        type = typeMessage.server;
                    }
                    else if (cmdspace[2].IndexOf('#') == 0 || cmdspace[2].IndexOf('&') == 0)
                    {
                        if (cmdColon[2].IndexOf('\x01') == 0)
                            type = flag ? typeMessage.ctcp : typeMessage.ctcpnotice;
                        else
                            type = flag ? typeMessage.chat : typeMessage.chatnotice;
                    }
                    else
                    {
                        type = flag ? typeMessage.privat : typeMessage.privatnotice;
                        AddPrivate(sourceNick);
                    }
                    SendMessage(new MessageArg(cmdSource, cmdspace[2], cmdColon[2], type));
                } // else if (cmd == "PRIVMSG" || cmd == "NOTICE")
                else if (cmd == "331" || cmd == "332") // топик
                {
                    foreach (Channel ch in channel)
                    {
                        if (0 == String.Compare(ch.Name, firstchan, true))
                        {
                            ch.Topic = cmdColon[2];
                            break;
                        }
                    }

                } // else if (cmd == "331" || cmd == "332")
                else if (cmd == "353") // names лист
                {

                    foreach (Channel ch in channel)
                    {
                        if (0 == String.Compare(ch.Name, firstchan, true))
                        {
                            ch.Users = cmdColon[2].Split(' ').ToList<string>();
                            break;
                        }
                    }

                }
                else if (cmd == "363") // end names лист
                {

                    foreach (Channel ch in channel)
                    {
                        if (0 == String.Compare(ch.Name, firstchan, true))
                        {
                            ch.EndOfUserList();
                            break;
                        }
                    }

                } // else if (cmd == "363") // end names лист
                else if (cmd == "PART")
                {
                    foreach (string partchan in firstchan.Split(','))
                    {
                        foreach (Channel ch in channel)
                        {
                            if( 0 == String.Compare(ch.Name, partchan,true))
                            {
                                ch.delUser(sourceNick);
                                break;
                            }
                        }
                    }
                } // else if (cmd == "PART")
                else if (cmd == "QUIT")
                {
                    foreach (Channel ch in channel)
                    {
                        if (ch.Users.IndexOf(sourceNick) != -1)
                        {
                            ch.delUser(sourceNick);
                        }
                    }
                }


                commands.RemoveAt(0);
                
            }
        }

        private void CloseServ(object sender, CancelEventArgs e)
        {
            while (channel.Count != 0)
            {
                channel[0].Close();
            }
            while (privat.Count != 0)
            {
                privat[0].Close();
            }
            ConnectThread.Abort();
            if (IRCConnect != null && IRCConnect.Connected)
            {
                writer.WriteLine("QUIT :" + quitMess);
                writer.Flush();
                IRCConnect.Close();
            }
            ownerForm.DelSwitchButton("System");
            
            ParsingThread.Abort();

        }
        //private void CreateForm(string text)
        //{
        //    FormChat form = new FormChat();
        //    // Set the Parent Form of the Child window.
        //    form.MdiParent = ownerForm;
        //    // Display the new form.
        //    form.Show();
        //    form.Text = text;
        //    chats.Add(form);
        //    chatNames.Add(text);

        //}
    }
}
